package FrameWork.Executor;

import org.apache.log4j.Logger;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

public class EventLoopGroup {

    private static Logger logger = Logger.getLogger(EventLoopGroup.class);
    private static final int processorAmount = 2*Runtime.getRuntime().availableProcessors()+1;
    private int loopAmount;
    private EventLoop[] eventLoops;
    private AtomicInteger counter = new AtomicInteger(0);
    private CountDownLatch latch;
    private volatile boolean on = false;

    public EventLoopGroup(){
        this(processorAmount);
    }

    public EventLoopGroup(int n){
        this.loopAmount = n;
    }

    public void init(){
        ThreadFactory factory = new EventLoopThreadFactory();
        eventLoops = new EventLoop[this.loopAmount];
        latch = new CountDownLatch(this.loopAmount);
        for (int i=0;i<this.loopAmount;i++){
            eventLoops[i] = new EventLoop(this,factory,i);
        }
        try {
            latch.await();
            logger.info("线程组初始化成功");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    void countDown(){
        latch.countDown();
    }

    public void submit(int dispatcherCode,String name,Runnable runnable){
        int idx = getProcessorIdx(dispatcherCode);
        EventLoop loop = eventLoops[idx];
        loop.submit(runnable);
    }

    public void submit(String name,Runnable runnable){
        EventLoop loop = getCurrentLoop();
        loop.submit(runnable);
    }

    public ScheduledFuture setTimer(int dispatcherCode,long delay,Runnable runnable){
        int idx = getProcessorIdx(dispatcherCode);
        EventLoop loop = eventLoops[idx];
        return loop.setTimer(delay,runnable);
    }

    public ScheduledFuture setTimer(long delay,Runnable runnable){
        EventLoop loop = getCurrentLoop();
        return loop.setTimer(delay,runnable);
    }

    public ScheduledFuture setPeriod(int dispatcherCode,long period,Runnable runnable){
        int idx = getProcessorIdx(dispatcherCode);
        EventLoop loop = eventLoops[idx];
        return loop.setPeriod(period,runnable);
    }

    public ScheduledFuture setPeriod(long period,Runnable runnable){
        EventLoop loop = getCurrentLoop();
        return loop.setPeriod(period,runnable);
    }

    private EventLoop getCurrentLoop(){
        Thread curr = Thread.currentThread();
        EventLoop loop;
        if (curr instanceof EventLoopThread){
            EventLoopThread eventLoopThread = (EventLoopThread)curr;
            loop = eventLoopThread.getEventLoop();
        }
        else{
            int idx = counter.incrementAndGet() % this.loopAmount;
            loop = eventLoops[idx];
        }
        return  loop;
    }

    public EventLoop getEventLoop(int code){
        return eventLoops[code%this.loopAmount];
    }

    public boolean inLoop(int code){
        Thread curr = Thread.currentThread();
        EventLoop loop;
        if (curr instanceof EventLoopThread){
            EventLoopThread eventLoopThread = (EventLoopThread)curr;
            if (eventLoopThread.getEventLoopGroup() != this){
                return false;
            }
            loop = eventLoopThread.getEventLoop();
            return loop == getEventLoop(code);
        }else{
            return false;
        }
    }

    private int getProcessorIdx(int code){
        code = code>=0?code:counter.incrementAndGet();
        return code%this.loopAmount;
    }

    public int getLoopAmount(){
        return loopAmount;
    }

}
